package module

import (
	"github.com/yuin/gopher-lua"
	"fmt"
	"encoding/json"
	"strings"
	"regexp"
	"reflect"
	"strconv"
	"linker/util/mysql"
	"database/sql"
)

type MysqlLink interface {
	Loader(L *lua.LState) int
}

type mysqlLink struct {
	db mysql.MysqlDB
}

func NewMysqlLink(db mysql.MysqlDB) MysqlLink {
	return &mysqlLink{db:db}
}
func(link *mysqlLink) Loader(L *lua.LState) int {
	exports := map[string]lua.LGFunction{
		"query":link.query,
		"page":link.paginate,
		"exec":link.exec,
		"tx":link.tx,
	}
	// register functions to the table
	mod := L.SetFuncs(L.NewTable(), exports)
	// register other stuff
	//L.SetField(mod, "name", lua.LString("value name"))

	// returns the module
	L.Push(mod)
	return 1
}



func(link *mysqlLink) query(L *lua.LState) int {

	sql:=L.CheckString(1)
	param:=make([]interface{},0)
	if L.Get(2)!=nil{
		paramString:=L.Get(2).String()
		if paramString!=""{
			json.Unmarshal([]byte(paramString),&param)
		}
	}
	res:=link.db.FindAll(sql,param...)

	str,_:=json.Marshal(res)
	L.Push(lua.LString(string(str)))

	return 1
}
func(link *mysqlLink) paginate(L *lua.LState) int {

	query:=L.CheckString(1)
	sqlExceptSelect:=L.CheckString(2)
	current:=L.CheckInt64(3)
	size:=L.CheckInt64(4)

	param:=make([]interface{},0)
	if L.Get(5)!=nil{
		paramString:=L.Get(5).String()
		if paramString!=""{
			json.Unmarshal([]byte(paramString),&param)
		}
	}
	res:=link.db.Paginate(query,sqlExceptSelect,current,size,param...)

	str,_:=json.Marshal(res)
	L.Push(lua.LString(string(str)))

	return 1
}
func(link *mysqlLink) exec(L *lua.LState) int {
	sql:=L.CheckString(1)
	param:=make([]interface{},0)
	if L.Get(2)!=nil{
		paramString:=L.Get(2).String()

		if paramString!=""{
			json.Unmarshal([]byte(paramString),&param)
		}
	}

	res,b:=link.db.Exec(sql,param...)

	if(!b){
		L.Push(lua.LBool(false))
	}else{
		str,_:=json.Marshal(res)
		L.Push(lua.LString(string(str)))
	}

	return 1
}
func(link *mysqlLink) tx(L *lua.LState) int {
	link.db.Tx(func(tx *sql.Tx) bool {
		table:=L.Get(1).(*lua.LTable)
		var data []interface{}
		result:=make([]map[string]interface{},0)
		b:=true
		table.ForEach(func(k lua.LValue, v lua.LValue) {
			if !b{
				return
			}
			p1:=v.(*lua.LTable)
			sql:=p1.RawGetString("sql").String()
			key:=p1.RawGetString("key").String()
			params:=sqlArgsParse(data,p1.RawGetString("param").String())

			if key=="query"{
				rows,err:=tx.Query(sql,params...)
				if err!=nil{
					fmt.Println(sql,err.Error())
					b=false
					return
				}
				list:=scanRows(rows)

				data=append(data,list)
			}else{

				ret,err:=tx.Exec(sql,params...)
				if err!=nil{
					fmt.Println(sql,err.Error())
					b=false
					return
				}
				m:=make(map[string]interface{})

				i1,err:=ret.LastInsertId()


				if err==nil{
					m["lastInsertId"]=i1
				}
				i2,err:=ret.RowsAffected()
				if err==nil{
					m["rowsAffected"]=i2
				}
				data=append(data,m)
				result=append(result,m)
			}

		})
		str,_:=json.Marshal(result)
		L.Push(lua.LString(string(str)))
		return b
	}, func() {

		L.Push(lua.LBool(false))
	}, func(err error) {
		L.Push(lua.LBool(false))
	})
	return 1
}

func sqlArgsParse(dataList []interface{},param string) []interface{} {

	reg:= regexp.MustCompile("(?U)#.+}")
	params:=make([]interface{},0)
	json.Unmarshal([]byte(param),&params)
	ret:=make([]interface{},0)
	if len(dataList)==0||len(params)==0{
		return params
	}
	for _,v:=range params{

		if(reflect.TypeOf(v).String()=="string"&&reg.Match([]byte(v.(string)))){
			ss:=strings.Replace(v.(string),"#{","",-1)
			ss=strings.Replace(ss,"}","",-1)
			reg2:= regexp.MustCompile("[(^r)\\d]+")
			dataIndex:=reg2.FindString(ss)
			if dataIndex==""{
				return params
			}
			dataIndexStr:=strings.Replace(dataIndex,"r","",-1)
			dataIndexAt,_:=strconv.ParseInt(dataIndexStr,10,32)
			data:=dataList[int(dataIndexAt)]
			reg= regexp.MustCompile("\\[\\d\\]")
			i:=reg.FindString(ss)
			if i==""{
				obj:=data.(map[string]interface{})
				plist:=strings.Split(ss,".")
				ret=append(ret,obj[plist[1]])
			}else{
				obj:=data.([]map[string]interface{})
				index:=strings.Replace(i,"[","",-1)
				index=strings.Replace(index,"]","",-1)
				indexAt,_:=strconv.ParseInt(index,10,32)

				plist:=strings.Split(ss,".")
				ret=append(ret,obj[int(indexAt)][plist[1]])
			}
		}else{
			ret=append(ret,v)
		}
	}

	return ret
}

func scanRows(rows *sql.Rows) []map[string]interface{} {
	defer rows.Close()

	columns, _ := rows.Columns()
	scanArgs := make([]interface{}, len(columns))
	values := make([][]byte, len(columns))
	records := []map[string]interface{}{}
	for i := range values {
		scanArgs[i] = &values[i]
	}

	for rows.Next() {

		//将行数据保存到record字典
		err := rows.Scan(scanArgs...)
		if err!=nil{

		}
		record := make(map[string]interface{})
		for i, col := range values {
			if col != nil {
				record[columns[i]] = string(col)
			}
		}
		records = append(records, record)
	}
	return records
}